﻿Imports System.Reflection
Imports System.Text
Imports Brainworklite.Core

<Serializable()> _
Public Class EntityBase

    <NonSerialized()> _
    Private _PropertiesAttributes As Dictionary(Of String, Attributes.PropertiesAttributes) = Nothing
    '<NonSerialized()> _
    'Private _ClassAttribute As SelfMappClassAttribute

    <NonSerialized()> _
    Public Const DataValueFieldName As String = "DataValueField"
    <NonSerialized()> _
    Public Const DataTextFieldName As String = "DataTextField"

    Private Function _getAttValue(Of T)(ByVal inf As PropertyInfo) As T
        Dim pDescAtt = inf.GetCustomAttributes(GetType(T), True)
        If pDescAtt IsNot Nothing AndAlso pDescAtt.Length > 0 Then
            Return pDescAtt(0)
        End If
        Return Nothing

    End Function

    Public Overrides Function ToString() As String
        Try
            Return Newtonsoft.Json.JsonConvert.SerializeObject(Me, Newtonsoft.Json.Formatting.None)
        Catch ex As Exception
            Return MyBase.ToString()
        End Try
    End Function

    <NonSerialized()> _
    Private _getPrimaryKeyProperty As PropertiesAttributes
    Public Function getPrimaryKeyProperty() As PropertiesAttributes
        If _getPrimaryKeyProperty Is Nothing Then
            Dim v = PropertiesAttributes.Where(Function(x) x.Value.PrimaryKeyAtt IsNot Nothing)
            If v.Any() Then
                Return v.FirstOrDefault().Value
            End If
        End If

        If _getPrimaryKeyProperty Is Nothing Then
            Throw New ApplicationException("No se ha marcado el attributo de la clave primaria")
        End If

        Return _getPrimaryKeyProperty
    End Function

    <NonSerialized()> _
    Private _getDisableProperty As PropertiesAttributes
    Public Function getDisableProperty() As PropertiesAttributes
        If _getDisableProperty Is Nothing Then
            Dim v = PropertiesAttributes.Where(Function(x) x.Value.DisableElementAtt IsNot Nothing)
            If v.Any() Then
                Return v.FirstOrDefault().Value
            End If
        End If



        Return _getDisableProperty
    End Function

    Public Function getCurrentPrimaryKeyValue() As Object
        Dim pk = getPrimaryKeyProperty()
        If Not pk Is Nothing Then
            Return pk.PropertyValue
        End If
        Return Nothing
    End Function

    <NonSerialized()> _
    Private _getDescriptionFields As List(Of PropertiesAttributes)
    Public Function getDescriptionFields() As List(Of PropertiesAttributes)
        Dim v = PropertiesAttributes.Where(Function(x) x.Value.DescriptionPartAtt IsNot Nothing)
        If v.Any() Then
            _getDescriptionFields = v.OrderBy(Function(x) x.Value.DescriptionPartAtt.Order).Select(Function(x) x.Value).ToList()
        Else

            v = PropertiesAttributes.Where(Function(x) x.Value.PropertyType.Name.Equals("string", StringComparison.InvariantCultureIgnoreCase))

            If v.Any() Then
                _getDescriptionFields = v.Select(Function(x) x.Value).ToList()
            Else
                If Me.getPrimaryKeyProperty() IsNot Nothing Then
                    Dim l As New List(Of PropertiesAttributes)
                    l.Add(Me.getPrimaryKeyProperty())
                    _getDescriptionFields = l
                End If
            End If
        End If

        Return _getDescriptionFields

    End Function

    <NonSerialized()> _
    Private _RequiredFields As List(Of PropertiesAttributes)
    Public Function getRequiredFields() As List(Of PropertiesAttributes)
        Dim v = PropertiesAttributes.Where(Function(x) x.Value.RequiredField IsNot Nothing AndAlso x.Value.PrimaryKeyAtt Is Nothing)

        If (v IsNot Nothing AndAlso v.Any()) Then
            Return v.Select(Function(pa) pa.Value).ToList()
        End If
        Return Nothing
    End Function

    Public Function getUniqueFields() As List(Of PropertiesAttributes)
        Dim v = PropertiesAttributes.Where(Function(x) x.Value.UniqueField IsNot Nothing AndAlso x.Value.PrimaryKeyAtt Is Nothing)

        If (v IsNot Nothing AndAlso v.Any()) Then
            Return v.Select(Function(pa) pa.Value).ToList()
        End If
        Return Nothing
    End Function



    <NonSerialized()> _
    Private _getListPartFields As List(Of PropertiesAttributes)
    Public Function getListPartFields() As List(Of PropertiesAttributes)
        If _getListPartFields Is Nothing Then
            Dim v = PropertiesAttributes.Where(Function(x) x.Value.isListViewPart IsNot Nothing)
            If v.Any() Then
                _getListPartFields = v.OrderBy(Function(x) x.Value.isListViewPart.Order).Select(Function(x) x.Value).ToList()
            Else
                _getListPartFields = Nothing
            End If
        End If

        Return _getListPartFields

    End Function

    Public Function getCurrentDescriptionValue() As String
        Dim dsc = getDescriptionFields()
        Dim sb As New StringBuilder()
        For Each item In dsc
            sb.AppendFormat("{0} ", item.PropertyValue)
        Next
        Return sb.ToString().Trim()
    End Function


    Public Function ClassAttribute() As SelfMappClassAttribute
        Dim key As String = String.Format("{0}.{1}", Me.GetType().FullName, "ClassAttribute")

        Dim _ClassAttribute = CacheMannager.getFromCache(Of EntityBase, SelfMappClassAttribute)(key)
        If _ClassAttribute Is Nothing Then
            Dim v = Me.GetType().GetCustomAttributes(GetType(SelfMappClassAttribute), True)
            If v.Any() Then
                _ClassAttribute = v(0)
                If String.IsNullOrEmpty(_ClassAttribute.TableName) Then
                    _ClassAttribute.TableName = Me.GetType().Name
                End If

                If String.IsNullOrEmpty(_ClassAttribute.BussinesLogicClassName) Then
                    _ClassAttribute.BussinesLogicClassName = "BussinesLogicGeneric(Of " & Me.GetType().FullName & ")"
                End If


            End If
            CacheMannager.SetInCache(Of EntityBase, SelfMappClassAttribute)(key, _ClassAttribute, 15)
        End If
        Return _ClassAttribute

    End Function


    Public Function getCascadeDeleteAttributes() As List(Of CascadeDelettingAttribute)
        Dim key = String.Format("{0}.{1}", Me.GetType().FullName, "getCascadeDeleteAttributes")
        Dim _getCascadeDeleteAttributes As List(Of CascadeDelettingAttribute)
        _getCascadeDeleteAttributes = CacheMannager.getFromCache(Of EntityBase, List(Of CascadeDelettingAttribute))(key)


        If _getCascadeDeleteAttributes Is Nothing Then
            _getCascadeDeleteAttributes = New List(Of CascadeDelettingAttribute)

            Dim v = Me.GetType().GetCustomAttributes(GetType(CascadeDelettingAttribute), True)
            If v.Any() Then
                For Each item In v
                    _getCascadeDeleteAttributes.Add(item)
                Next
            End If
            CacheMannager.SetInCache(Of EntityBase, List(Of CascadeDelettingAttribute))(key, _getCascadeDeleteAttributes, 30)
        End If
        Return _getCascadeDeleteAttributes
    End Function



    '<NonSerialized()> _
    'Dim _propertiesInfo As PropertyInfo()

    Public Function GetProperties() As PropertyInfo()
        Dim key = String.Format("{0}.{1}", Me.GetType().FullName, "GetProperties")

        Dim _propertiesInfo As PropertyInfo() = CacheMannager.getFromCache(Of EntityBase, PropertyInfo())(key)

        If _propertiesInfo Is Nothing Then

            _propertiesInfo = Me.GetType().GetProperties().Where(Function(x) _
                                                                   Not x.Name.Equals(DataTextFieldName) _
                                                                   AndAlso Not x.Name.Equals(DataValueFieldName) _
                                                                   AndAlso _getAttValue(Of Attributes.NotMappedAttribute)(x) Is Nothing).ToArray()
            CacheMannager.SetInCache(Of EntityBase, PropertyInfo())(key, _propertiesInfo, 30)
        End If
        Return _propertiesInfo
    End Function


    Public Function GetNotMappedProperties() As PropertyInfo()
        Return Me.GetType().GetProperties().Where(Function(x) _getAttValue(Of Attributes.NotMappedAttribute)(x) IsNot Nothing).ToArray()
    End Function



    'Public Shared Function getChildClassName(ByVal instance As EntityBase) As String
    '    Return instance.GetType().FullName
    'End Function


    'todo: cachear los attributos de cada clase
    Private ppa As New Dictionary(Of String, Dictionary(Of String, Attributes.PropertiesAttributes))
    Private Property _cachedPropertiesAttributes As Dictionary(Of String, Attributes.PropertiesAttributes)
        Get
            Dim clsName = Me.GetType().FullName
            If ppa.ContainsKey(clsName) Then
                Return ppa(clsName)
            End If
            Return Nothing
        End Get
        Set(ByVal value As Dictionary(Of String, Attributes.PropertiesAttributes))
            Dim clsName = Me.GetType().FullName
            If ppa.ContainsKey(clsName) Then
                ppa(clsName) = value
            Else
                ppa.Add(clsName, value)
            End If
        End Set
    End Property

    Public Function PropertiesAttributes() As Dictionary(Of String, Attributes.PropertiesAttributes)
        ' Dim o = New StackFrame().GetMethod().DeclaringType()

        If _PropertiesAttributes Is Nothing Then
            _PropertiesAttributes = New Dictionary(Of String, Attributes.PropertiesAttributes)

            Dim key As String = String.Format("{0}.{1}", Me.GetType().FullName, "PropertiesAttributes")
            Dim cpa = CacheMannager.getFromCache(Of EntityBase, Dictionary(Of String, Attributes.PropertiesAttributes))(key)

            _cachedPropertiesAttributes = cpa
            If _cachedPropertiesAttributes Is Nothing Then
                _cachedPropertiesAttributes = New Dictionary(Of String, Attributes.PropertiesAttributes)

                Dim AllPropertyInfo = GetProperties().Where(Function(x) x.CanRead AndAlso x.CanWrite)
                For Each p In AllPropertyInfo


                    Dim alcua = p.GetCustomAttributes(True)
                    Dim listaDeTodosLosTiposDeAttributos = alcua.Select(Function(x) x.GetType())


                    Dim nuevoPa As New Attributes.PropertiesAttributes
                    nuevoPa.FieldMappingAtt = New FieldMappingAttribute(p.Name)
                    nuevoPa.PropertyName = p.Name
                    nuevoPa.PropertyType = p.PropertyType
                    '

                    For Each nuevoPApI In nuevoPa.GetType().GetProperties().Where(Function(x) x.CanWrite)

                        If listaDeTodosLosTiposDeAttributos.Contains(nuevoPApI.PropertyType) Then
                            Dim nuevoAttt = nuevoPApI.PropertyType
                            For Each item In alcua.Where(Function(x) x.GetType().Equals(nuevoAttt))
                                SetEntityValue(nuevoPa, nuevoPApI.Name, item)
                            Next

                        End If
                    Next



                    'Todo: buscar el nombre de la clase contenedora de esta colección
                    'con ese nombre, buscar en el ensamblado correspondiente a este espacio
                    'de nombres la clase con la que empieza este nombre especial
                    'buscar sus attributes y encontrar el mapeo! (por defecto será la clase primaria)
                    'Persona_Collection

                    'If isCollectionAtt Is Nothing Then
                    '    'If p.Name.EndsWith("_Collection", StringComparison.InvariantCultureIgnoreCase) Then
                    '    '    'isCollectionAtt = New IsCollectionAttribute(p.Name.Split("_Collection")(0),)
                    '    'End If
                    'End If

                    _cachedPropertiesAttributes.Add(p.Name, nuevoPa)
                Next
                CacheMannager.SetInCache(Of EntityBase, Dictionary(Of String, Attributes.PropertiesAttributes))(key, _cachedPropertiesAttributes, 30)

            End If

            For Each PropName As String In _cachedPropertiesAttributes.Keys
                Dim nuevoPa As New Attributes.PropertiesAttributes
                nuevoPa = _cachedPropertiesAttributes(PropName).Clone()
                _PropertiesAttributes.Add(PropName, nuevoPa)
            Next

        End If


        For Each PropName As String In _cachedPropertiesAttributes.Keys
            Dim nuevoPa As Attributes.PropertiesAttributes = _PropertiesAttributes(PropName)
            nuevoPa.PropertyValue = ReflectionHelper.GetEntityValue(Me, PropName)
        Next

        Return _PropertiesAttributes
    End Function

    <NotMapped()> _
    Public ReadOnly Property DataValueField As String
        Get
            Return CStr(Me.getCurrentPrimaryKeyValue)
        End Get
    End Property

    <NotMapped()> _
    Public ReadOnly Property DataTextField As String
        Get
            Return getCurrentDescriptionValue()
        End Get
    End Property

    'Private Function Newtonsoft() As Object
    '    Throw New NotImplementedException
    'End Function


End Class
